#include <FileSystem.h>
#include <EDesignerCore.h>
#include <ObjectInspectorItem.h>
#include <iostream>
#include <QFileDialog>
#include <QTextStream>
#include <fstream>
#include <streambuf>
#include <sstream>
#include <EObject.h>
#include <QMetaProperty>
#include <Functions.h>
#include <JlCompress.h>
FileSystem::FileSystem()
{
    m_domDocument = new QDomDocument();
}

FileSystem::~FileSystem()
{

}

bool FileSystem::newFile(const QString &file_path)
{

    bool boolValue = false;
    /*
    QDir *temp = new QDir(file_path);
    bool exist = temp->exists("modelData");
    if(exist)
    {
        boolValue = true;
        m_currentDir = file_path;
    }
    else
    {
        bool ok = temp->mkdir("modelData");
        if( ok )
        {
            std::cout<<"FileSystem::newFile creat modeldata"<<std::endl;
            boolValue = true;
            m_currentDir = file_path;
        }
    }*/
    //clear project dir
    clearProjectDir();
    creatModelDataDirInProjectDir();
    saveFile(file_path);
    m_currentFilePath = file_path;
    return true;
}

bool FileSystem::newFileByDialog()
{
    QString filePath = QFileDialog::getSaveFileName(0, "new file","","");
    return newFile(filePath);

}

void FileSystem::saveFile(const QString &file_path)
{
    EDesignerCore::instance()->simulationWaitUpdateEndAndStopSim();

    beforeSaveFile();

    m_domDocument->clear();

    QStandardItem* rootItem = EDesignerCore::instance()->getRootItemPtr();
    rootItem->setText("rootItem");
    //std::cout<<"rootItem has children:"<<rootItem->rowCount()<<std::endl;

    QDomElement rootDomElement = m_domDocument->createElement("Document");
    rootDomElement.appendChild( itemToDomElement(rootItem) );
    m_domDocument->appendChild(rootDomElement );


    //writ xml file to project dir
    QFile file(QDir::currentPath()+QString("/project/project.xml"));
    file.open(QIODevice::WriteOnly);
    file.close();
    if(file.open(QIODevice::WriteOnly | QIODevice::Text) == false)
    {
        std::cout<<"save xml filed"<<std::endl;
    }
    QTextStream out(&file);
    out << m_domDocument->toString();
    file.close();

    //compress to the zip file
    JlCompress::compressDir(file_path, QDir::currentPath()+QString("/project"));

    m_currentFilePath = file_path;


    EDesignerCore::instance()->simulationstopWaitUpdateEnd();
}

void FileSystem::saveFileByDialog()
{
    QString filePath = QFileDialog::getSaveFileName(0, "Save File","","*.zip");
    saveFile(filePath);
}

void FileSystem::saveSimulationState()
{
    EDesignerCore::instance()->simulationWaitUpdateEndAndStopSim();

    m_domDocument->clear();

    QStandardItem* rootItem = EDesignerCore::instance()->getRootItemPtr();
    rootItem->setText("rootItem");
    //std::cout<<"rootItem has children:"<<rootItem->rowCount()<<std::endl;

    QDomElement rootDomElement = m_domDocument->createElement("Document");
    rootDomElement.appendChild( itemToDomElementForSimState(rootItem) );
    m_domDocument->appendChild(rootDomElement );


    //writ xml file to filePath
    QFile file(QDir::currentPath()+QString("/tempFiles/beforeSimStateSaved.xml"));
    file.open(QIODevice::WriteOnly);
    file.close();
    if(file.open(QIODevice::WriteOnly | QIODevice::Text) == false)
    {
        std::cout<<"FileSystem::saveSimulationState: "<<"save xml filed"<<std::endl;
    }
    QTextStream out(&file);
    out << m_domDocument->toString();
    file.close();

    EDesignerCore::instance()->simulationstopWaitUpdateEnd();
}

void FileSystem::loadFile(const QString& filePath)
{
    EDesignerCore::instance()->simulationWaitUpdateEndAndStopSim();

    //clear the project dir
    clearProjectDir();



    //judge the file suffix
    QFileInfo fileInfo(filePath);
    if(fileInfo.suffix() != "zip")
    {
        std::cout<<"FileSystem::loadFile: Error-The file is not a zip file"<<std::endl;
        return;
    }

    //unzip the zip file
    std::cout<<"FileSystem::loadFile extractDir"<<std::endl;
    JlCompress::extractDir(filePath, QDir::currentPath()+QString("/project"));

    //set current file path
    m_currentFilePath = filePath;

    //read xml file
    QString file_path = QDir::currentPath()+QString("/project/project.xml");
    QFile file(file_path);

    if(file.open(QIODevice::ReadOnly | QIODevice::Text) == false)
    {
        std::cout<<"load xml filed"<<std::endl;
        return;
    }
    QTextStream in(&file);
    QString xmlFileContext = in.readAll();
    file.close();

    beforeLoadFile();
/**/
    m_domDocument->clear();
    m_domDocument->setContent(xmlFileContext,false);

    //std::cout<<m_domDocument->toString().toStdString()<<std::endl;
    QDomElement doc = m_domDocument->documentElement();
    //std::cout<<"tagName = "<<root.tagName().toStdString()<<std::endl;
    QDomNodeList domNodeList = doc.firstChild().childNodes();
    QStandardItem* rootItem = EDesignerCore::instance()->getRootItemPtr();
    for(int i = 0; i < domNodeList.size(); i++)
    {
        QStandardItem* childItemPtr = domElementToItem(domNodeList.at(i).toElement(), rootItem);
        //if(childItemPtr != NULL)
        //{
          //  rootItem->appendRow(childItemPtr);
        //}
    }

    afterLoadFile();



    EDesignerCore::instance()->simulationstopWaitUpdateEnd();
}

void FileSystem::loadFileByDialog()
{
    QString file_name = QFileDialog::getOpenFileName(0, "Load File","","*.zip");
    loadFile(file_name);
}

void FileSystem::loadSimulationState()
{
    EDesignerCore::instance()->simulationWaitUpdateEndAndStopSim();

    //read xml file
    QString file_path = QDir::currentPath()+QString("/tempFiles/beforeSimStateSaved.xml");
    QFile file(file_path);

    if(file.open(QIODevice::ReadOnly | QIODevice::Text) == false)
    {
        std::cout<<"FileSystem::loadSimulationState: load xml filed"<<std::endl;
        return;
    }
    QTextStream in(&file);
    QString xmlFileContext = in.readAll();
    file.close();


/**/
    m_domDocument->clear();
    m_domDocument->setContent(xmlFileContext,false);

    //std::cout<<m_domDocument->toString().toStdString()<<std::endl;
    QDomElement doc = m_domDocument->documentElement();
    //std::cout<<"tagName = "<<root.tagName().toStdString()<<std::endl;
    QDomNodeList domNodeList = doc.firstChild().childNodes();
    QStandardItem* rootItem = EDesignerCore::instance()->getRootItemPtr();
    for(int i = 0; i < domNodeList.size(); i++)
    {
        QStandardItem* childItemPtr = domElementToItemForSimState(domNodeList.at(i).toElement(), rootItem);
        //if(childItemPtr != NULL)
        //{
          //  rootItem->appendRow(childItemPtr);
        //}
    }

    EDesignerCore::instance()->simulationstopWaitUpdateEnd();
}

void FileSystem::addFileSystemObject(QSharedPointer<FileSystemObject> fileObject)
{
    if(fileObject.data() == NULL)
    {
        return;
    }
    m_fileObjectList.push_back(fileObject);
}

void FileSystem::beforeSaveFile()
{
    int i = 0;
    for(i = 0 ; i < m_fileObjectList.size(); i++)
    {
        m_fileObjectList[i]->beforeSaveFile();
    }
}

void FileSystem::beforeLoadFile()
{

    //call plugins "beforeLoadFile"
    EDesignerCore::instance()->objectInspectorWindow()->clear();
    int i = 0;
    for(i = 0 ; i < m_fileObjectList.size(); i++)
    {
        m_fileObjectList[i]->beforeLoadFile();
    }
}

void FileSystem::afterLoadFile()
{
    int i = 0;
    for(i = 0 ; i < m_fileObjectList.size(); i++)
    {
        m_fileObjectList[i]->afterLoadFile();
    }
}

QString FileSystem::getCurrentProjectDir()
{
    return m_currentDir;
}

QString FileSystem::getCurrentFilePath()
{
    return m_currentFilePath;
}

QDomElement FileSystem::itemToDomElement(QStandardItem *item)
{
    //std::cout<<item->text().toStdString()<<std::endl;
    QDomElement domElement;
    //get the object information
    if(item->text() != "rootItem")
    {
        ObjectInspectorItem* objectItem = dynamic_cast<ObjectInspectorItem*>(item);
        if(objectItem == NULL)
        {
            domElement = m_domDocument->createElement("");
        }
        else
        {
            QObject* objectPtr = objectItem->getObjectPtr().data();

            domElement = m_domDocument->createElement(objectPtr->metaObject()->className());
            for(int i = 0; i < objectPtr->metaObject()->propertyCount(); i ++)
            {
                QMetaProperty property = objectPtr->metaObject()->property(i);
                //property name

                //property value
                QVariant variant = objectPtr->property(property.name());

                domElement.setAttribute( QString(property.name()), EEditorFunctions::QVariantToQString(variant) );
            }


            if(objectItem->getObjectPtr()->isTraverseChildEObject() )
            {
                for(int i = 0; i < item->rowCount(); i++)
                {
                    QDomNode childDomElement = itemToDomElement(item->child(i));
                    domElement.appendChild(childDomElement);
                }
            }

        }
    }
    else
    {
        //for the rootItem
        domElement = m_domDocument->createElement(item->text());

        for(int i = 0; i < item->rowCount(); i++)
        {
            QDomNode childDomElement = itemToDomElement(item->child(i));
            domElement.appendChild(childDomElement);
        }
    }







    return domElement;
}

QDomElement FileSystem::itemToDomElementForSimState(QStandardItem *item)
{
    QDomElement domElement;
    //get the object information
    if(item->text() != "rootItem")
    {
        ObjectInspectorItem* objectItem = dynamic_cast<ObjectInspectorItem*>(item);
        if(objectItem == NULL)
        {
            domElement = m_domDocument->createElement("");
        }
        else
        {
            QObject* objectPtr = objectItem->getObjectPtr().data();
            EObject* eObjectPtr = dynamic_cast<EObject*>(objectPtr);
            domElement = m_domDocument->createElement(objectPtr->metaObject()->className());
            if(eObjectPtr != NULL)
            {
                QVariant variant((int)eObjectPtr);
                domElement.setAttribute( "ptr", EEditorFunctions::QVariantToQString(variant) );
            }
            for(int i = 0; i < objectPtr->metaObject()->propertyCount(); i ++)
            {
                QMetaProperty property = objectPtr->metaObject()->property(i);
                if(eObjectPtr != NULL && eObjectPtr->isRecoverProperty(property))
                {
                    //property value
                    QVariant variant = objectPtr->property(property.name());
                    domElement.setAttribute( QString(property.name()), EEditorFunctions::QVariantToQString(variant) );
                }

            }
            if(objectItem->getObjectPtr()->isTraverseChildEObject() )
            {
                for(int i = 0; i < item->rowCount(); i++)
                {
                    QDomNode childDomElement = itemToDomElementForSimState(item->child(i));
                    domElement.appendChild(childDomElement);
                }
            }

        }
    }
    else
    {
        //for the rootItem
        domElement = m_domDocument->createElement(item->text());

        for(int i = 0; i < item->rowCount(); i++)
        {
            QDomNode childDomElement = itemToDomElementForSimState(item->child(i));
            domElement.appendChild(childDomElement);
        }
    }
    return domElement;

}

QStandardItem *FileSystem::domElementToItem(const QDomElement& domElement, QStandardItem *parentItem)
{
    ObjectInspectorItem* newItem = EDesignerCore::instance()->objectInspectorWindow()->addDown(parentItem, domElement.tagName());
    if(newItem != NULL)
    {


        //copy property
        EObject* newObj = newItem->getObjectPtr().data();

        QDomNamedNodeMap  attributes = domElement.attributes();
        for(int i = 0 ; i < attributes.size(); i++)
        {
            QDomAttr attr = attributes.item(i).toAttr();
            //std::cout<<"FileSystem::domElementToItem"<<attr.name().toStdString()<<" = "<<attr.value().toStdString()<<std::endl;

            int indexOfProperty = newObj->metaObject()->indexOfProperty(attr.name().toStdString().c_str());
            if(indexOfProperty < 0)
            {
                continue;
            }
            QMetaProperty property = newObj->metaObject()->property(indexOfProperty);
//std::string name(property.name());
            if(property.isWritable())
                newObj->setProperty(property.name(), EEditorFunctions::QStringToQVariant(property.type(), attr.value()));

        }

        //complete newObj by plugins
        for(int i = 0; i < m_fileObjectList.size(); i++)
        {
            m_fileObjectList.at(i)->completeObjByDom(newObj, domElement);
        }

        //find child element
        QDomNodeList domNodeList = domElement.childNodes();
        for(int i = 0; i < domNodeList.size(); i++)
        {
            QStandardItem* childItemPtr = domElementToItem(domNodeList.at(i).toElement(), newItem);

        }
    }

    return newItem;
}

QStandardItem *FileSystem::domElementToItemForSimState(const QDomElement &domElement, QStandardItem *parentItem)
{
    //get QObject form attribute
    QDomNamedNodeMap  attributes = domElement.attributes();
    QDomAttr attrPtr = attributes.namedItem("ptr").toAttr();//attributes.item(0).toAttr();
    int intPtr = attrPtr.value().toInt();
    EObject* eObjPtr = (EObject*)intPtr;//dynamic_cast<EObject*>(intPtr);
    if(eObjPtr != NULL)
    {
        //std::cout<<"FileSystem::domElementToItemForSimState: "<<eObjPtr->getEObjectName().toStdString()<<std::endl;
    }
    for(int i = 0 ; i < attributes.size(); i++)
    {
        QDomAttr attr = attributes.item(i).toAttr();

        int indexOfProperty = eObjPtr->metaObject()->indexOfProperty(attr.name().toStdString().c_str());
        if(indexOfProperty < 0)
        {
            continue;
        }
        QMetaProperty property = eObjPtr->metaObject()->property(indexOfProperty);
        eObjPtr->setProperty(property.name(), EEditorFunctions::QStringToQVariant(property.type(), attr.value()));

    }


    //travel all child node
    QDomNodeList domNodeList = domElement.childNodes();
    for(int i = 0; i < domNodeList.size(); i++)
    {
        QStandardItem* childItemPtr = domElementToItemForSimState(domNodeList.at(i).toElement(), parentItem);

    }

    return parentItem;
}

void FileSystem::clearProjectDir()
{
    //just remove "modelData" dir, remove xml file, this function may be rewriten

    //std::cout<<"FileSystem::clearProjectDir"<<(QDir::currentPath()+ QString("/project")).toStdString()<<std::endl;
    removeFolderContent(QDir::currentPath()+ QString("/project/modelData"));

}

bool FileSystem::removeFolderContent(const QString &folderDir)
{
    QDir dir(folderDir);
        QFileInfoList fileList;
        QFileInfo curFile;
        if(!dir.exists())  {return false;}//文件不存，则返回false
        fileList=dir.entryInfoList(QDir::Dirs|QDir::Files
                                   |QDir::Readable|QDir::Writable
                                   |QDir::Hidden|QDir::NoDotAndDotDot
                                   ,QDir::Name);
        while(fileList.size()>0)//跳出条件
        {
            int infoNum=fileList.size();
            for(int i=infoNum-1;i>=0;i--)
            {
                curFile=fileList[i];
                if(curFile.isFile())//如果是文件，删除文件
                {
                    QFile fileTemp(curFile.filePath());
                    fileTemp.remove();
                    fileList.removeAt(i);
                }
                if(curFile.isDir())//如果是文件夹
                {
                    QDir dirTemp(curFile.filePath());
                    QFileInfoList fileList1=dirTemp.entryInfoList(QDir::Dirs|QDir::Files
                                                                  |QDir::Readable|QDir::Writable
                                                                  |QDir::Hidden|QDir::NoDotAndDotDot
                                                                  ,QDir::Name);
                    if(fileList1.size()==0)//下层没有文件或文件夹
                    {
                        dirTemp.rmdir(".");
                        fileList.removeAt(i);
                    }
                    else//下层有文件夹或文件
                    {
                        for(int j=0;j<fileList1.size();j++)
                        {
                            if(!(fileList.contains(fileList1[j])))
                                fileList.append(fileList1[j]);
                        }
                    }
                }
            }
        }
        return true;
}

void FileSystem::creatModelDataDirInProjectDir()
{
    //creat modelData Dir In ProjectDir
    QDir dir;
    dir.mkdir(QDir::currentPath() + QString("/project/modelData"));
}
/*
Json::Value FileSystem::itemToJsonValue(ObjectInspectorItem *item)
{
    Json::Value jsonValue;
    return jsonValue;
}
*/
